-
Notifications
You must be signed in to change notification settings - Fork 366
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add packetcapture api #6257
base: main
Are you sure you want to change the base?
Add packetcapture api #6257
Conversation
efdfde8
to
1f9e3c5
Compare
moved API to this MR. Thanks you previous feedback and welcome to help review this again. @luolanzone @tnqn @jianjuns @antoninbas Accoding to #5821 (comment) . |
pkg/apis/crd/v1alpha1/types.go
Outdated
// SrcPort is the source port. | ||
SrcPort int32 `json:"srcPort,omitempty"` | ||
// DstPort is the destination port. | ||
DstPort int32 `json:"dstPort,omitempty"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know how @jianjuns and @tnqn will feel about this, but since this is a new API, I think we should follow the K8s convention:
Therefore, we ask that pointers always be used with optional fields that do not have a built-in nil value.
It tends to lead to a lot of pointers, but it makes it clearer what is optional and what is not. When creating a resource with YAML, it makes no difference. When creating a resource in Go, we can use ptr.To
from the K8s libraries to make initialization less verbose. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed. There maybe a small problem if we upgrade to v1beta1 later to share the same structure with Traceflow though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rather do the best interface possible now for PacketCapture
, and worry about unifying later.
If we have to introduce a new v1beta2
version for Traceflow, it should still be possible to define a conversion webhook to convert between v1beta1
and v1beta2
, because of the semantics of the field?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated. Can you help review this change? I'm not very sure if i have done it right.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think @antoninbas means:
type UDPHeader struct {
// SrcPort is the source port.
SrcPort *int32 `json:"srcPort,omitempty"`
// DstPort is the destination port.
DstPort *int32 `json:"dstPort,omitempty"`
}
because they are optional.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just saw newer comments. Please ignore it.
4b65b01
to
9567b89
Compare
pkg/apis/crd/v1alpha1/types.go
Outdated
// SrcPort is the source port. | ||
SrcPort int32 `json:"srcPort,omitempty"` | ||
// DstPort is the destination port. | ||
DstPort int32 `json:"dstPort,omitempty"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rather do the best interface possible now for PacketCapture
, and worry about unifying later.
If we have to introduce a new v1beta2
version for Traceflow, it should still be possible to define a conversion webhook to convert between v1beta1
and v1beta2
, because of the semantics of the field?
packet: | ||
type: object | ||
properties: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it looks like we are missing the srcIP
/ dstIP
properties here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's actually not missed, since we have a Source
and Destination
field in spec
, so the srcIP/dstIP
is not used. The golang structure still keep these fields in the PR, but i think we can removed them. The whole Packet
structure has changed a lot during the review compared to the original one(==Traceflow's Packet strucuture).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated this part. Currently the package related structure looks like this:
// Packet includes header info.
type Packet struct {
IPv6Header *IPv6Header `json:"ipv6Header,omitempty"`
TransportHeader TransportHeader `json:"transportHeader"`
}
Note: remove IPv4Header
as it's unused and in another thread, we are discussing if a IPFamily
field is needed.
Also in the TransportHeader
, we have tcp/icmp/udp strcuture to allow user filter based on transport attributes. For icmp, we don't have any filter yet, so the strucutre is mainly used as a type indicator.
type ICMPEchoRequestHeader struct {
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cc @jianjuns @tnqn @luolanzone
Can you help review this MR again? Thank you
I will be actively working on this recently.
9567b89
to
7d8d4b1
Compare
// Pod is the source pod. | ||
Pod string `json:"pod"` | ||
// IP is the source IPv4 or IPv6 address. | ||
IP *string `json:"ip,omitempty"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this field mutually exclusive with Namespace
/ Pod
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not exactly. we don't have this kind of check now. This IP
field is used for Pod->IP
or IP->Pod
filter case.
The only requirement is that at least one pod is specified.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So it would be valid to provide both a destination Pod and a destination IP (which don't match)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As long as there is one pod present(no matter whether it's in the source or destination), it will work(ignore the ip field in the same endpoint). I didn't add strict validation for the IP related case, not sure what's the best approach.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
since it's exclusive , i have added crd validation using oneOf
in the yaml. User cannot provide ip
and pod
both in the source/dest struct in there CR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@antoninbas found a little problem with this whole omitempty
thing.
Since i'm using oneOf
to perform some validation in the crd, so the related field need to be set to omitempty
wether they are pointer or value or not , or the validation will fail. (using yaml is fine, but golang struct is not, eg in tests code)
For example. If i want to use oneOf for source.ip/pod, means only one can be spcified. the following wont work
type Source struct {
Namespace string `json:"namespace"`
Pod string `json:"pod"`
IP string `json:"ip"`
}
spec: crdv1alpha1.PacketCaptureSpec{
Source: crdv1alpha1.Source{
Namespace: "test-namespace",
Pod: "test-pod",
},
since there is no omitempty
it's actually:
spec: crdv1alpha1.PacketCaptureSpec{
Source: crdv1alpha1.Source{
Namespace: "test-namespace",
Pod: "test-pod",
IP: "",
},
So the oneOf
validation will fail. The solution would be change every affected field (including pod
) to pointer + omitempty. or keep as value + omitempty. If not set, it won't show up.
Currently i kept the value format and add some of the omitempty
tag back, as change to pointer would require some changes on the current codebase. If you still felt this is needed ( > pointer), i can update this later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
well I think that in K8s this would be something like:
type Source {
SourcePod *PodReference `json:"sourcePod,omitempty"`
SourceIP *string `json:"sourceIP,omitempty"`
}
type Destination {
DestinationPod *PodReference `json:"destinationPod,omitempty"`
DestinationService *ServiceReference `json:"destinationService,omitempty"`
DestinationIP *string `json:"destinationIP,omitempty"`
}
Technically the pointers are not strictly necessary for the IP fields, but I like the symmetry.
It does make the API more verbose though, cc @tnqn
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
now that we have addressed the issue, could you put back the oneOf
s in the CRD OpenAPI schema?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sure ,already did.
Destination Destination `json:"destination"` | ||
Packet *Packet `json:"packet,omitempty"` | ||
// FileServer specifies the sftp url config for the fileServer. Captured packets will be uploaded to this server. | ||
FileServer BundleFileServer `json:"fileServer"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like we should have the option to write the pcap to a local file as well, just for ease of use / testing
Not everyone will want to have an ftp server for this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So do you think keep the files in container is enough? Since this is not a cli, it would be odd to export the files to a location on the host.
If keep it in the container, filepath + container name should be ok?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If keep it in the container, filepath + container name should be ok
Just filepath I think, the container will always be antrea-agent
? If users want the file to be on the Node, they can provide a path that is mounted from the host, such as /var/log/antrea
.
But there would need to be a way for users to figure out which antrea-agent Pod has the capture file, so that would need to be included in the Status. Are packets captured at the source or the destination or both?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Both could happen, in the source or destination. pod name can be exposed in the status. Just need to think about the value format for the filepath field.
SrcPort *int32 `json:"srcPort,omitempty"` | ||
// DstPort is the destination port. | ||
DstPort *int32 `json:"dstPort,omitempty"` | ||
} | ||
|
||
// TCPHeader describes spec of a TCP header. | ||
type TCPHeader struct { | ||
// SrcPort is the source port. | ||
SrcPort *int32 `json:"srcPort,omitempty"` | ||
// DstPort is the destination port. | ||
DstPort *int32 `json:"dstPort,omitempty"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know I suggested pointers for this originally. Given more recent discussions, I don't really feel strongly about it anymore, so given that 0 is not a valid port number typically, I would be ok with removing the pointers. I'll let @tnqn chime in as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
temporary reset to int32 type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My first impression is this should have a pointer because it's optional. if header.SrcPort != nil
is the condition in which we should capture specifc source port only, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, it's optional. sounds more reasonable to set it as pointer.
|
||
type PacketCapturePhase string | ||
|
||
const ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it possible to share some types/constants with Traceflow?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also see discussion #6257 (comment)
for the constants part i think it's unnecessary, each CRD may have more different status in the future.
About types, since Traceflow
is in vebeta1
now, are you suggesting we define some common types in neutral place?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure if we should define common types across versions. Let us see if @tnqn and @antoninbas have an opinion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess in theory we could have common definitions in a neutral directory, it wouldn't need to be under apis/crd/<version>
. But if it's not a versioned directory, then we would have to be confident that they are not going to change.
K8s has meta/v1
with comm types, such as Time
. I don't know how good of an analogy it is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Except those meta types in meta/v1. K8s doesn't define common types across versions. If a constant needs to be used by a version, it will just be replicated in that version. I think it's to keep every version's stability and compatability. Otherwise a change in one version would have an impact on other versions.
@jianjuns Do you think we can add a ip family and ip protocol field? so we can remove ipv6header and icmpheader. |
That works for me, if you do not see a potential requirement to support other IP header fields, esp. v4 or v6 specific fields. |
updated. |
|
||
type PacketCapturePhase string | ||
|
||
const ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure if we should define common types across versions. Let us see if @tnqn and @antoninbas have an opinion.
2c563b3
to
57f6640
Compare
pkg/apis/crd/v1alpha1/types.go
Outdated
// SrcPort is the source port. | ||
SrcPort int32 `json:"srcPort,omitempty"` | ||
// DstPort is the destination port. | ||
DstPort int32 `json:"dstPort,omitempty"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think @antoninbas means:
type UDPHeader struct {
// SrcPort is the source port.
SrcPort *int32 `json:"srcPort,omitempty"`
// DstPort is the destination port.
DstPort *int32 `json:"dstPort,omitempty"`
}
because they are optional.
|
||
type PacketCapturePhase string | ||
|
||
const ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Except those meta types in meta/v1. K8s doesn't define common types across versions. If a constant needs to be used by a version, it will just be replicated in that version. I think it's to keep every version's stability and compatability. Otherwise a change in one version would have an impact on other versions.
7363edc
to
3ba4312
Compare
Any new comments? |
default: IPv4 | ||
protocol: | ||
x-kubernetes-int-or-string: true | ||
enum: [ICMP, TCP, UDP, IPv6-ICMP, 1, 6, 17, 58] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it feels like for packet capture, we don't need to have a fixed list of protocols? we could filter on an arbitrary protocol value
also SCTP is missing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i didn't add support for SCTP yet. will try to work on that. or maybe this is not a must-have feature in this time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My main point was not about SCTP specifically, but about whether we really need to support a fixed list of protocols, as opposed to supporting a common set of names, but also letting users provide an arbitrary protocol number.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sure. removed this for now and i will double check the impl code and add more tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if i missed anything here, but the type code defined in this file doesn't have SCTP.
// Pod is the source pod. | ||
Pod string `json:"pod"` | ||
// IP is the source IPv4 or IPv6 address. | ||
IP *string `json:"ip,omitempty"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
well I think that in K8s this would be something like:
type Source {
SourcePod *PodReference `json:"sourcePod,omitempty"`
SourceIP *string `json:"sourceIP,omitempty"`
}
type Destination {
DestinationPod *PodReference `json:"destinationPod,omitempty"`
DestinationService *ServiceReference `json:"destinationService,omitempty"`
DestinationIP *string `json:"destinationIP,omitempty"`
}
Technically the pointers are not strictly necessary for the IP fields, but I like the symmetry.
It does make the API more verbose though, cc @tnqn
Signed-off-by: Hang Yan <yhang@vmware.com>
3ba4312
to
46467e1
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm curious what the packet number, phase, reason, timestamp mean when the target flow involves two Nodes, and how the two agents coordinate.
pkg/apis/crd/v1alpha1/types.go
Outdated
NumCapturedPackets *int32 `json:"numCapturedPackets,omitempty"` | ||
// PacketsFileName is the file name where the captured packets are temporarily cached. The file will be | ||
// removed after the PacketCapture is deleted. | ||
PacketsFileName string `json:"packetsFileName"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does the file refer to exactly? The file uploaded to fileserver, or the local file in pod?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's a filename for fileserver and also in local container. After the CR is deleted, local file will be deleted too.
Also @antoninbas suggested maybe add container (in which pod) info to the path so we can also support only save the file in the container thus don't require a fileserver. this may also require some spec
changes(make fileserver optional)
pkg/apis/crd/v1alpha1/types.go
Outdated
Phase PacketCapturePhase `json:"phase"` | ||
// Reason records the failure reason when the capture fails. | ||
Reason string `json:"reason"` | ||
// NumCapturedPackets records how many packets have been captured. If it reaches the target number, the capture | ||
// can be considered as finished. | ||
NumCapturedPackets *int32 `json:"numCapturedPackets,omitempty"` | ||
// PacketsFileName is the file name where the captured packets are temporarily cached. The file will be | ||
// removed after the PacketCapture is deleted. | ||
PacketsFileName string `json:"packetsFileName"` | ||
StartTime *metav1.Time `json:"startTime,omitempty"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does StartTime represent and which component is responsible for setting it when the source and destination Pods are on two Nodes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Source. It represents the start time of the capture since we have a timeout
settings in the spec, so this field can be used to check whether this capture has been timeout. I will update the comments to make this more clear.
Reason string `json:"reason"` | ||
// NumCapturedPackets records how many packets have been captured. If it reaches the target number, the capture | ||
// can be considered as finished. | ||
NumCapturedPackets *int32 `json:"numCapturedPackets,omitempty"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- When the source and destination Pod are on different Node, will it capture packets on both Nodes?
- N means the total of the packets on two Nodes or each?
- What if packets can be captured on source Node and not on destination Node?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No. All sessions will only performing the capture in one node, either in the source or the destination. I think the location doesn't matter so this was hidden from the api spec.
Phase PacketCapturePhase `json:"phase"` | ||
// Reason records the failure reason when the capture fails. | ||
Reason string `json:"reason"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure a single Phase and Reason are enough for all conditions that it can occur when capturing packets on two Nodes and uploading to a server, maybe it will need a list conditions to represent each condition and respective reason and timestamp. But hard to say what are needed until it's implemented, we can defer to the implementation patch.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you mean conditions like Traceflow
? Currently i didn't implement this in PacketCapture, not sure if this is needed.
@antoninbas I don't find place to reply your comment at #6257 (comment), so starting one here. I get the points of XXXReference and pointer, but is it common to have the struct name in the names of its members? It seems useful when the structs are inline properties in their parents but seems redundant otherwise. |
No you're right, I am not sure why I added the |
If there are two nodes, only the source node is used. Since the user is only cared about the final result, i think use either node is fine. |
Can one of the admins verify this patch? |
4 similar comments
Can one of the admins verify this patch? |
Can one of the admins verify this patch? |
Can one of the admins verify this patch? |
Can one of the admins verify this patch? |
updated. yeah this seems neater. |
IPFamily v1.IPFamily `json:"ipFamily"` | ||
// Protocol represents the transport protocol. default to ICMP(1). Other | ||
// possible choices are: TCP(6), UDP(17). | ||
Protocol *intstr.IntOrString `json:"protocol,omitempty"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it feels like the default should be "don't filter on protocol", and not ICMP
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
make sense. updated.
pkg/apis/crd/v1alpha1/types.go
Outdated
// Packet includes header info. | ||
type Packet struct { | ||
// IPFamily is the filter's IP family. Default to `IPv4`. | ||
IPFamily v1.IPFamily `json:"ipFamily"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: maybe add omitempty
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated.
packet: | ||
type: object | ||
x-kubernetes-validations: | ||
- rule: "(self.ipFamily == 'IPv4' && self.protocol != 'IPv6-ICMP' && self.protocol != 58) || (self.ipFamily == 'IPv6' && self.protocol != 'ICMP' && self.protocol != 1) " |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this doesn't seem correct unless I am missing something. Protocol is not limited to ICMP.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed this validation rule as it seems unnecessary for now.
// Pod is the source pod. | ||
Pod string `json:"pod"` | ||
// IP is the source IPv4 or IPv6 address. | ||
IP *string `json:"ip,omitempty"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
now that we have addressed the issue, could you put back the oneOf
s in the CRD OpenAPI schema?
pkg/apis/crd/v1alpha1/types.go
Outdated
// IPFamily is the filter's IP family. Default to `IPv4`. | ||
IPFamily v1.IPFamily `json:"ipFamily"` | ||
// Protocol represents the transport protocol. default to ICMP(1). Other | ||
// possible choices are: TCP(6), UDP(17). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
comment no longer up-to-date
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated.
Signed-off-by: Hang Yan <yhang@vmware.com>
Add packetcapture's API changes.
For the full feature, please ref to:
#5821